/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 * 
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

/**
 * @constructor
 */
Messages = function() {

}
Messages.prototype._createBitField = function(pieceManager) {
	var body = this._createBitFieldBody(pieceManager);
	// body plus one for the message type byte (message type is 5, bitfield)
	return encodeIntTo4Char(body.length + 1) + String.fromCharCode(5) + body;
}
Messages.prototype._createBitFieldBody = function(pieceManager) {
	var i;
	var current = 0;
	var bytes = [128,64,32,16,8,4,2,1];
	var result = "";
	for (i=0; i < pieceManager.getPieceCount(); i++) {
		if ((i % 8) == 0 && i != 0) {
			result = result + String.fromCharCode(current);
			current = 0;
		}
		if (pieceManager.havePiece(i)) {
			current = current | bytes[i % 8];
		}
	}
	// Do the last byte. Note this _always_ has to be done,
	// the looped version only adds to result when we start
	// a new byte - thus the loop always ends with the
	// current byte in progress.
	result = result + String.fromCharCode(current);
	return result;
}
Messages.prototype.bitfield = function(pieceManager) {
    // Just skip the createRemoteMsg pattern for this one
	//return this._createRemoteMsg(5);
    return this._createBitField(pieceManager);
}
Messages.prototype.request = function(index,offset,length) {
	return this._createRemoteMsg(6,index,offset,length);
}
Messages.prototype.keepAlive = function() {
	return this._createRemoteMsg(-1);
}
Messages.prototype.have = function(piece) {
	return this._createRemoteMsg(4,piece);
}
Messages.prototype.interested = function() {
	return this._createRemoteMsg(2);
}
Messages.prototype.notInterested = function() {
	return this._createRemoteMsg(3);
}
Messages.prototype.choke = function() {
	return this._createRemoteMsg(0);
}
Messages.prototype.unchoke = function() {
	return this._createRemoteMsg(1);
}
Messages.prototype.piece = function(index,begin,length,pieceManager) {
	if (index < pieceManager.getPieceCount()
		&& pieceManager.havePiece(index)
		) {
		if ((begin + length) <= pieceManager.getPieceLength(index)) {
			return this._createRemoteMsg(7,index,begin,
                ArrayToString(pieceManager.getPartialPiece(index,begin,length)) 
            );
		} else {
			return this._createRemoteMsg(7,index,begin,
                ArrayToString(pieceManager.getPartialPiece(index,begin,length)) + 
				ArrayToString(pieceManager.getPartialPiece(index+1,0,length - (pieceManager.getPieceLength(index) - begin)))
				);
		}
	}
	if (index >= pieceManager.getPieceCount()) {
		throw Components.Exception("Messages.piece Error: caused by index >= pieceManager.getPieceCount()");
	} else if (!pieceManager.havePiece(index)) {
		throw Components.Exception("Messages.piece Error: caused by !pieceManager.havePiece(index)");
	}
}

Messages.prototype._createRemoteMsg = function(messageId) {
	//var messageId = arguments[0];
	
	var msg = "";
	switch (messageId) {
	case -1:
		/* special case: keep-alive has no messageId */
		msg += String.fromCharCode(0x00,0x00,0x00,0x00); // 4 bytes
		break;
	case 0:
		/* choke */
		msg += String.fromCharCode(0x00,0x00,0x00,0x01); // 4 bytes
		msg += String.fromCharCode(0x00); // id
		break;
	case 1:
		/* unchoke */
		msg += String.fromCharCode(0x00, 0x00, 0x00, 0x01); // 4 bytes
		msg += String.fromCharCode(0x01); // id
		break;
	case 2:
		/* interested */
		msg += String.fromCharCode(0x00, 0x00, 0x00, 0x01); // 4 bytes
		msg += String.fromCharCode(0x02); // id
		break;
	case 3:
		/* not interested */
		msg += String.fromCharCode(0x00, 0x00, 0x00, 0x01); // 4 bytes
		msg += String.fromCharCode(0x03); // id
		break;
	case 4:
		/* have */
		msg += String.fromCharCode(0x00, 0x00, 0x00, 0x05); // 4 bytes
		msg += String.fromCharCode(0x04); // id
		msg += encodeIntTo4Char(arguments[1]);
		//msg += String.fromCharCode(0x00, 0x0Peer.prototype.sendUnchoke = function() {
		break;
	case 5:
		/* bitfield */
		msg += this._createBitField();
		//msg += String.fromCharCode(0x00, 0x00, 0x00, 0x01); // 4 bytes value needs to be 1 +x
		//msg += String.fromCharCode(0x05); // id
		break;
	case 6:
		/* request */
		msg += String.fromCharCode(0x00, 0x00, 0x00, 0x0d /* 13 */); // 4 bytes value needs to be 1 +x
		msg += String.fromCharCode(0x06); // id		
		msg += encodeIntTo4Char(arguments[1]); // index
		msg += encodeIntTo4Char(arguments[2]); // begin
		msg += encodeIntTo4Char(arguments[3]); // length
		break;
	case 7:
		/* piece */
		msg += encodeIntTo4Char(9 + arguments[3].length); // 4 bytes value needs to be 1 +x
		msg += String.fromCharCode(0x07); // id		
		msg += encodeIntTo4Char(arguments[1]); // index
		msg += encodeIntTo4Char(arguments[2]); // begin
		msg += arguments[3]; // block
		break;
	case 8:
		/* cancel */
		msg += encodeIntTo4Char(13); // 4 bytes value needs to be 1 +x
		msg += String.fromCharCode(0x08); // id		
		msg += encodeIntTo4Char(arguments[1]); // index
		msg += encodeIntTo4Char(arguments[2]); // begin
		msg += encodeIntTo4Char(arguments[3]); // length
		break;

	case 9:
		/* port */
		msg += encodeIntTo4Char(5); // 4 bytes value needs to be 1 +x
		msg += String.fromCharCode(0x09); // id		
		msg += encodeIntTo4Char(arguments[1]); // port
		// this could be wrong. wiki says 2 bytes for port.
		break;
	}
	if (msg.length == 0) {
		throw Components.Exception("Messages._createRemoteMsg : ERROR : Msg length is 0 when creating msg id: " + messageId);
	}

	return msg;
}

var NSGetModule = XPCOMUtils.generateNSGetModule([Messages]);

